package com.lin.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lin.blog.constant.ArticleConstant;
import com.lin.blog.constant.LikedStatusConstant;
import com.lin.blog.constant.UserConstant;
import com.lin.blog.entity.Article;
import com.lin.blog.entity.ArticleTags;
import com.lin.blog.mapper.ArticleCategoryTagsMapper;
import com.lin.blog.mapper.ArticleMapper;
import com.lin.blog.mapper.ArticleTagsMapper;
import com.lin.blog.service.IArticleService;
import com.lin.blog.vo.ArticleCategoryTagsVO;
import com.lin.constant.ArticleSearchMapConstant;
import com.lin.constant.RedisConstant;
import com.lin.constant.ResultCode;
import com.lin.mq.MQConstant;
import com.lin.utils.ListUtils;
import com.lin.utils.RedisUtils;
import com.lin.utils.Result;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author lin
 * @since 2020-03-08
 */
@Service
@CacheConfig(cacheNames = "article")
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements IArticleService {

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private ArticleTagsMapper articleTagsMapper;

    @Autowired
    private ArticleCategoryTagsMapper articleCategoryTagsMapper;


    /**
     * 通过主键ID查询ArticleContent
     * @param id
     * @return
     */
    @Override
    @Cacheable(key = "'content::'+#id",unless = "#result.code!=200")
    public Result getArticleContent(Long id) {
        String content = null;
        // 参数不为空
        if (id == null) {
            return Result.failure(ResultCode.PARAM_IS_BLANK);
        }
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("id",id);
        queryWrapper.select("content");
        Article article = articleMapper.selectOne(queryWrapper);
        if (article!=null){
            content = article.getContent();
        }
        return Result.success(content);
    }


    /**
     * 插入一条Article记录
     * @param article
     * @return
     */
    @Override
    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public Result saveArticle(Article article) throws Exception {

        article.setLook(0);
        article.setLikes(0);
        article.setLevel(0);
        article.setCreateTime(LocalDateTime.now());
        article.setUpdateTime(LocalDateTime.now());
        if (articleMapper.insert(article) > 0){

            // 插入ArticleTags
            if (!StringUtils.isEmpty(article.getTagsList())) {
                articleTagsMapper.insertBatch(article.getId(), article.getTagsList());
            }


            return Result.success(article.getId());
        }
        return Result.failure(ResultCode.INSERT_ERROR);
    }




    /**
     * 通过主键ID删除Article
     * @param id
     * @return
     */
    @CacheEvict(allEntries = true)
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result removeArticle(Long id) throws Exception {
        // 参数不为空
        if (id == null) {
            return Result.failure(ResultCode.PARAM_IS_BLANK);
        }
        if (articleMapper.deleteById(id) > 0) {
            // 删除文章_标签映射
            articleTagsMapper.deleteByArticleId(id);

            return Result.success();
        }
        return Result.failure(ResultCode.DELETE_ERROR);
    }


    /**
     * 通过主键ID更新Article
     * @param id
     * @param article
     * @return
     */

    @CacheEvict(allEntries = true)
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result updateArticle(Long id, Article article) throws Exception {
        // 参数不为空
        if (id == null) {
            return Result.failure(ResultCode.PARAM_IS_BLANK);
        }
        article.setId(id);
        // 设置更新
        article.setUpdateTime(LocalDateTime.now());
        // 设置排除更新字段  排除 user_id level likes look create_time

        article.setLevel(null);
        article.setLikes(null);
        article.setLook(null);

        if (articleMapper.updateById(article) > 0) {
            // 根据 articleId 修改ArticleTags数据库信息
            modifyArticleTags(id, article);
            return Result.success(article);
        }
        return Result.failure(ResultCode.UPDATE_ERROR);
    }

    /**
     * 获取ArticleList时间线
     * @return
     */
    @Cacheable(key = "'articleTimeLine'",unless = "#result.code!=200")
    @Override
    public Result getArticleTimeLine() {
        QueryWrapper<Article> queryWrapper = new QueryWrapper();
        queryWrapper.select("id","title","author","create_time");
        queryWrapper.orderByDesc("create_time");
        return Result.success(articleMapper.selectList(queryWrapper));
    }


    /**
     * 增加文章浏览
     * @param articleId
     * @return
     */
    @Override
    public Result incrementArticleLooks(Long articleId) {
        // 参数不为空
        if (articleId == null) {
            return Result.failure(ResultCode.PARAM_IS_BLANK);
        }
        String key = articleId.toString();
        /**
         * 查看Redis中是否存在
         */

        Boolean articleLooksIsInRedis = RedisUtils.opsForHash().hasKey(RedisConstant.MAP_KEY_ARTICLE_LOOKS_COUNT,key);
        if (articleLooksIsInRedis == true){
            RedisUtils.opsForHash().increment(RedisConstant.MAP_KEY_ARTICLE_LOOKS_COUNT,key,1);
        }else{
            RedisUtils.hPut(RedisConstant.MAP_KEY_ARTICLE_LOOKS_COUNT,key,1);
        }
        return Result.success();
    }

    /**
     * 将Redis里的文章浏览数据存入数据库中
     */
    @Override
    @Transactional
    public void transArticleLooksFromRedis2DB() {
        Map<Object, Object> entries = RedisUtils.opsForHash().entries(RedisConstant.MAP_KEY_ARTICLE_LOOKS_COUNT);
        Iterator<Map.Entry<Object, Object>> iterator = entries.entrySet().iterator();
        while (iterator.hasNext()){
            // 解析出 key value
            Map.Entry entry = iterator.next();
            Long articleId = Long.valueOf((String) entry.getKey());
            Integer looks =Integer.valueOf((Integer) entry.getValue());

            // 更新数据库
            articleMapper.increaseArticleLooksById(articleId,looks);

            // 删除Redis中该数据
            RedisUtils.hdel(RedisConstant.MAP_KEY_ARTICLE_LOOKS_COUNT, (String) entry.getKey());
        }

    }



    /**
     * 根据 articleId 修改ArticleTags数据库信息
     * @param id
     * @param article
     */
    private void modifyArticleTags(Long id, Article article) {
        // 修改ArticleTags
        // 前端发送的TagsList
        List<Long> tagsList = article.getTagsList();

        if (!StringUtils.isEmpty(tagsList)){
            // 数据库获取的TagsList
            List<Long> tagsListFromDb = new ArrayList<>();
            List<ArticleTags> articleTagsList = articleTagsMapper.getArticleTagListByArticleId(id);
            for (ArticleTags tags:
                    articleTagsList) {
                tagsListFromDb.add(tags.getTagsId());
            }

            // 比较
            if (!isListEquals(tagsList,tagsListFromDb)) {

                // 删除列表 则 前端传输的tagsList 不存在  数据库的 tagsListFromDB存在
                List<Long> deleteTagsList  =  ListUtils.getDifference(tagsListFromDb,tagsList);

                // 增加列表 则 前端传输的tagsList 存在,  数据库的 tagsListFromDB不存在
                List<Long> insertTagsList  =  ListUtils.getDifference(tagsList,tagsListFromDb);


                // 批量删除添加
                if (deleteTagsList.size()!=0){
                    articleTagsMapper.deleteBatch(id,deleteTagsList);
                }

                // 批量添加元素
                if (insertTagsList.size()!=0){
                    articleTagsMapper.insertBatch(id,insertTagsList);
                }

            }


        }
    }





    /**
     * 判断两列表是否相等
     * @param tagsList
     * @param tagsListFromDb
     * @return
     */
    private boolean isListEquals(List<Long> tagsList, List<Long> tagsListFromDb) {
        Collections.sort(tagsList);
        Collections.sort(tagsListFromDb);
        return tagsList.equals(tagsListFromDb);
    }


}
